home *** CD-ROM | disk | FTP | other *** search
/ Adobe Graphics & Publishing SDK 1996 December / Adobe Graphics & Publishing SDK 1996 December.iso / mac / After Effects 3.1 SDK Mac / Examples / Effects Samples / Gamma (Table) / GammaTable.c < prev   
C/C++ Source or Header  |  1996-11-06  |  13KB  |  459 lines

  1. /**
  2.     GammaTable.c
  3.     
  4.     Part of the Adobe After Effects 3.1 SDK.
  5.     Copyright (c)1993-96, Adobe Systems Inc, All Rights Reserved.
  6.  
  7.     This effect does a simple gamma correction of the image pixels.
  8.     It allocates space for a lookup table in the sequence data,
  9.     and fills that lookup table with the gamma corrected values.
  10.  
  11.     This is a lot faster because:
  12.         If the table already contains the correct values, it does
  13.             not need to be regenerated for each frame.  This means
  14.             making a movie where a series of frames all have the
  15.             same gamma correction is very quick.
  16.         We only have to process 256 values and then can uses that
  17.             for each channel of every pixel.  This makes it faster
  18.             than the GammaNoTable effect even for a single frame.
  19.  
  20.     This demonstrates:
  21.         using a sequence data Handle
  22.         iterating over the image pixels (w/o using the Iterate callback)
  23.         calling the progress callback
  24.         a Fixed Slider control
  25.         the Extent Hint rectangle (from the InData structure)
  26.         making the application display an error alert for us
  27.         using the PF_ITERATE callback
  28.  
  29.     Revision History
  30.         1.0, created by rb, 12 March 93
  31.         1.1, updated for AE 2.0, dmw, 11 Jan 94
  32.         1.2, updated for Mac/PowerMac, dmw, 13 Oct 94
  33.         2.0, updated for AE 3.0, dmw, 18 Oct 95
  34.         2.1, Added call to AEFX_CLR_STRUCT macro to clear out PF_ParamDef, ba, 6 Nov 96
  35. **/
  36.  
  37. #include "AE_EffectCB.h"
  38. #include "AE_Macros.h"
  39. #include <A4Stuff.h>
  40.  
  41. #define    MAJOR_VERSION        2
  42. #define    MINOR_VERSION        0
  43. #define    BUG_VERSION            0
  44. #define    STAGE_VERSION        PF_Stage_RELEASE
  45. #define    BUILD_VERSION        0
  46.  
  47. #define    NAME    "Gamma (Table)"
  48.  
  49.  
  50. /** Parameter Definition Constants
  51.  
  52.     Here we define the parameters, their default
  53.     settings, and minimum and maximum values.
  54.  
  55. **/
  56.  
  57. enum {
  58.     GAMMA_INPUT = 0,    /* params index of default input layer */
  59.     GAMMA_GAMMA,        /* params index of gamma correction factor */
  60.  
  61.     GAMMA_NUM_PARAMS
  62. };
  63.  
  64.  
  65. /* We'll allow the gamma correction factor slider vary
  66.  * from 0.1 to 5.0, with the default being at 1.0...
  67.  * If the user brings up the direct number entry dialog,
  68.  * we'll let them type in a value up to 10.0, even though
  69.  * the slider values will stop at 5.0...
  70.  * All of these numbers are in Fixed point.
  71.  */
  72.  
  73. #define    GAMMA_MIN        (6553)    /* approximately 0.1 in fixed point */
  74. #define    GAMMA_MAX        (5L << 16)
  75. #define    GAMMA_BIG_MAX    (10L << 16)
  76. #define    GAMMA_DFLT        (1L << 16)
  77.  
  78.  
  79. /** Command Specific Subroutines
  80.  
  81.     This plug-in only deals with the commands:
  82.         PF_Cmd_ABOUT
  83.         PF_Cmd_GLOBAL_SETUP
  84.         PF_Cmd_GLOBAL_SETDOWN
  85.         PF_Cmd_PARAMS_SETUP
  86.         PF_Cmd_SEQUENCE_SETUP
  87.         PF_Cmd_SEQUENCE_RESETUP
  88.         PF_Cmd_SEQUENCE_SETDOWN
  89.         PF_Cmd_RENDER
  90.     All other commands are ignored.  There is a routine for
  91.     each command, and a main routine to dispatch at the bottom.
  92.  
  93. **/
  94.  
  95. #define DESCRIPTION    "Perform simple image gamma correction."
  96.  
  97. static PF_Err About (
  98.     PF_InData        *in_data,
  99.     PF_OutData        *out_data,
  100.     PF_ParamDef        *params[],
  101.     PF_LayerDef        *output )
  102. {
  103.     PF_SPRINTF(out_data->return_msg, "%s, v%d.%d\r%s",
  104.                NAME, MAJOR_VERSION, MINOR_VERSION, DESCRIPTION);
  105.  
  106.     return PF_Err_NONE;
  107. }
  108.  
  109.  
  110. static PF_Err GlobalSetup (
  111.     PF_InData        *in_data,
  112.     PF_OutData        *out_data,
  113.     PF_ParamDef        *params[],
  114.     PF_LayerDef        *output )
  115. {
  116.     PF_Err    err = PF_Err_NONE;
  117.  
  118.     /* Need to let AE know what version of the "Gamma" plug-in
  119.      * we are...... */
  120.      
  121.     out_data->my_version = PF_VERSION(MAJOR_VERSION, MINOR_VERSION,
  122.                                       BUG_VERSION, STAGE_VERSION, BUILD_VERSION);
  123.  
  124.     /* We are only going to iterate over the visible pixels,
  125.      * i.e. the extent_hint rect, so we need to specify the
  126.      * flag for the output extent (you can use the input extent
  127.      * rect without telling AE.) 
  128.      *
  129.      * Each result pixel in the output buffer depends only on the value of
  130.      * the corresponding input pixel, so I'll also set the PIX_INDEPENDENT OutFlag,
  131.      * which can speed up the rendering of field-rendered animations tremendously.
  132.      * Be careful when setting this flag, though; if you set it when it's not true,
  133.      * field rendered output can be quite wrong.
  134.      */
  135.      
  136.     out_data->out_flags |=
  137.         PF_OutFlag_PIX_INDEPENDENT |
  138.         PF_OutFlag_USE_OUTPUT_EXTENT;
  139.  
  140.     return err;
  141. }
  142.  
  143.  
  144. static PF_Err GlobalSetdown (
  145.     PF_InData        *in_data,
  146.     PF_OutData        *out_data,
  147.     PF_ParamDef        *params[],
  148.     PF_LayerDef        *output )
  149. {
  150.     /* We haven't actually allocated any global data,
  151.      * so we don't need to do anything here...
  152.      */
  153.      
  154.     return PF_Err_NONE;
  155. }
  156.  
  157.  
  158. static PF_Err ParamsSetup (
  159.     PF_InData        *in_data,
  160.     PF_OutData        *out_data,
  161.     PF_ParamDef        *params[],
  162.     PF_LayerDef        *output )
  163. {
  164.     PF_Err            err = PF_Err_NONE;
  165.     PF_ParamDef        def;    /* scratch space for a parameter definition */
  166.  
  167.     /* Always clear out the PF_ParamDef structure before adding your parameters,
  168.      * this macro will do that.
  169.      */
  170.     AEFX_CLR_STRUCT(def);
  171.     
  172.     /* Create the FIXED SLIDER parameter... */
  173.  
  174.     def.param_type = PF_Param_FIX_SLIDER;
  175.     PF_STRCPY(def.name, "Gamma");
  176.     
  177.     /* NOTE: we must set these strings to empty strings to prevent
  178.      * garbage text from being displayed above our sliders... */
  179.      
  180.     def.u.fd.value_str[0] = def.u.fd.value_desc[0] = '\0';
  181.     def.u.fd.value = def.u.fd.dephault = GAMMA_DFLT;
  182.     
  183.     /* The min value of the slider and the min value the user
  184.      * can type in will both be the same value... */
  185.      
  186.     def.u.fd.valid_min = def.u.fd.slider_min = GAMMA_MIN;
  187.     
  188.     /* The max value of the slider will be smaller than the
  189.      * max value the user can type in.  This way, the slider
  190.      * has a nice feel to it, but the user who really wants
  191.      * strange results can access larger values... */
  192.      
  193.     def.u.fd.slider_max = GAMMA_MAX;
  194.     def.u.fd.valid_max = GAMMA_BIG_MAX;
  195.     
  196.     /* Let's let the user see 1 decimal place of precision. */
  197.     
  198.     def.u.fd.precision = 1;
  199.     def.u.fd.display_flags = 0;
  200.  
  201.     if (err = PF_ADD_PARAM(in_data, -1, &def)) return err;
  202.  
  203.     /* Set number of parameters before leaving */
  204.  
  205.     out_data->num_params = GAMMA_NUM_PARAMS;
  206.  
  207.     return err;
  208. }
  209.  
  210.  
  211. /* Gamma Lookup Table
  212.  *
  213.  * We store a gamma lookup table in the sequence data so that
  214.  * each sequence, if rendered in a make movie, can keep its
  215.  * own table and can reuse it from frame to frame.  The sequence
  216.  * data stores the lookup table and the gamma that is stored
  217.  * in that lookup table. */
  218.  
  219. typedef struct {
  220.     Fixed            gamma_val;    /* what is the gamma currently in the table */
  221.     unsigned char    lut[256];    /* the lookup table */
  222. } GammaTable;
  223.  
  224.  
  225. static PF_Err SequenceSetup (
  226.     PF_InData        *in_data,
  227.     PF_OutData        *out_data,
  228.     PF_ParamDef        *params[],
  229.     PF_LayerDef        *output )
  230. {
  231.     GammaTable        *g_table;
  232.     long            i;
  233.  
  234.     out_data->sequence_data = NewHandle(sizeof(GammaTable));
  235.  
  236.     if (!out_data->sequence_data) return (PF_Err)memFullErr;
  237.  
  238.     /* generate base table */
  239.  
  240.     HLock(out_data->sequence_data);
  241.     g_table = *(GammaTable **)out_data->sequence_data;
  242.  
  243.     g_table->gamma_val = (1L << 16);
  244.     for (i=0; i<=255; i++) g_table->lut[i] = (i << 16);
  245.  
  246.     HUnlock(out_data->sequence_data);
  247.  
  248.     /* remember to tell us the size of your sequence data */
  249.  
  250.     out_data->flat_sdata_size = sizeof(GammaTable);
  251.  
  252.     return PF_Err_NONE;
  253. }
  254.  
  255.  
  256. static PF_Err SequenceSetdown (
  257.     PF_InData        *in_data,
  258.     PF_OutData        *out_data,
  259.     PF_ParamDef        *params[],
  260.     PF_LayerDef        *output )
  261. {
  262.     if (in_data->sequence_data) {
  263.         DisposHandle(in_data->sequence_data);
  264.         out_data->sequence_data = NULL;
  265.     }
  266.     return PF_Err_NONE;
  267. }
  268.  
  269.  
  270. static PF_Err SequenceResetup (
  271.     PF_InData        *in_data,
  272.     PF_OutData        *out_data,
  273.     PF_ParamDef        *params[],
  274.     PF_LayerDef        *output )
  275. {
  276.     /* Just to be paranoid, if somehow we get a Resetup and the
  277.      * table is not allocated, then let's try again to allocate it. */
  278.      
  279.     if (!in_data->sequence_data) {
  280.         return SequenceSetup(in_data, out_data, params, output);
  281.     }
  282.  
  283.     return PF_Err_NONE;
  284. }
  285.  
  286.  
  287. /** This is a structure we'll pass to the function we use with the PF_ITERATE
  288.  ** callback; it's just a pointer to the gamma lookup table. **/
  289.  
  290. typedef struct {
  291.     unsigned char        *lut;
  292. } GammaInfo;
  293.  
  294.  
  295. /** Here's our iterator func. It just computes the gamma-corrected pixel
  296.  ** given the lookup table.
  297.  **/
  298.  
  299. static PF_Err GammaFunc (long refcon, long x, long y, PF_Pixel *in, PF_Pixel *out)
  300. {
  301.     PF_Err        err = PF_Err_NONE;
  302.     GammaInfo    *gi;    
  303.     
  304.     gi = (GammaInfo *)refcon;
  305.  
  306.     out->alpha = in->alpha;
  307.     out->red = gi->lut[ in->red ];
  308.     out->green = gi->lut[ in->green ];
  309.     out->blue = gi->lut[ in->blue ];
  310.  
  311.     return err;
  312. }
  313.  
  314.  
  315. /* Render
  316.  *        To render our image, we will iterate over all the pixels,
  317.  *        leaving the alpha untouched, and looking up the RGB values
  318.  *        in the gamma correction lookup table.  We will generate
  319.  *        that table, if we need to, at the start of this routine.
  320.  *        You will note that 68881 code generation is OFF. */
  321.  
  322. static PF_Err Render ( 
  323.     PF_InData        *in_data,
  324.     PF_OutData        *out_data,
  325.     PF_ParamDef        *params[],
  326.     PF_LayerDef        *output )
  327. {
  328.     PF_Err            err = PF_Err_NONE;
  329.     register long    x;
  330.     long            progress_height;
  331.     GammaTable        *g_table;
  332.     GammaInfo        gamma_info;
  333.     
  334.     /* If the gamma factor is exactly 1.0, just copy and return */
  335.     
  336.     if (params[GAMMA_GAMMA]->u.fd.value == (1L << 16)) {
  337.         err = PF_COPY(¶ms[0]->u.ld, output, NULL, NULL);
  338.  
  339.     } else {
  340.  
  341.         /* If there is no allocated table, let's bring up an error message.
  342.          * In a release version of this effect, since the table is so small,
  343.          * it would make more sense to create it on the stack and continue,
  344.          * even if the Handle was not allocated.  But here we'll show how
  345.          * to report an error to the user... */
  346.          
  347.         if (!out_data->sequence_data) {
  348.             PF_STRCPY(out_data->return_msg, "Gamma effect invoked without lookup table");
  349.             out_data->out_flags |= PF_OutFlag_DISPLAY_ERROR_MESSAGE;
  350.             return PF_Err_INTERNAL_STRUCT_DAMAGED;
  351.         }
  352.     
  353.         HLock(out_data->sequence_data);
  354.         g_table = *(GammaTable **)out_data->sequence_data;
  355.     
  356.         if (g_table->gamma_val != params[GAMMA_GAMMA]->u.fd.value) {
  357.         
  358.             /* if the table doesn't contain the right gamma values,
  359.              * we need to regenerate the table contents... */
  360.              
  361.             double    temp, gamma;
  362.     
  363.             g_table->gamma_val = params[GAMMA_GAMMA]->u.fd.value;
  364.             gamma = (double)g_table->gamma_val / (double)(1L << 16);
  365.             
  366.             gamma = 1.0/gamma;
  367.             
  368.             for (x=0; x<=255; x++) {
  369.                 temp = PF_POW((double)x / 255.0, gamma);
  370.                 g_table->lut[x] = (unsigned char)(temp * 255.0);
  371.             }
  372.         }
  373.     
  374.         /* See the comment in GammaNoTable.c about why we must fill the
  375.          * output extent with alpha zero if it does not equal the extent
  376.          * over which we are going to iterate. */
  377.          
  378.         if (in_data->extent_hint.left != output->extent_hint.left ||
  379.             in_data->extent_hint.top != output->extent_hint.top ||
  380.             in_data->extent_hint.right != output->extent_hint.right ||
  381.             in_data->extent_hint.bottom != output->extent_hint.bottom) {
  382.     
  383.             err = PF_FILL(NULL, &output->extent_hint, output);
  384.         }
  385.     
  386.         if (!err) {
  387.         
  388.             /* Instead of setting up the for loops and incrementing pointers,
  389.              * let's use the PF_ITERATE callback. The PF_ITERATE callback
  390.              * is pretty smart; it unrolls loops so there's not too much overhead
  391.              * in using it, and it can greatly simplify your code -- especially
  392.              * if you're just marching over a rectangle. It will automatically call
  393.              * the PF_PROGRESS for you, and in the future it might even parallelize
  394.              * things for you.
  395.              *
  396.              * Here we're just iterating over the in_data->extent_hint. We send in the
  397.              * height of the extent hint rectangle as the number of steps the progess
  398.              * bar will take, and I send in a pointer to my gamma_info structure and the
  399.              * address of the actual gamma correction routune. */
  400.         
  401.             progress_height = in_data->extent_hint.top - in_data->extent_hint.bottom;
  402.             gamma_info.lut = g_table->lut;
  403.             
  404.             err = PF_ITERATE(0, progress_height,
  405.                     ¶ms[GAMMA_INPUT]->u.ld, &in_data->extent_hint,
  406.                     (long)&gamma_info, GammaFunc, output);
  407.         
  408.         }
  409.     
  410.         HUnlock(out_data->sequence_data);
  411.  
  412.     }
  413.     return err;
  414. }
  415.  
  416.  
  417. PF_Err main (
  418.     PF_Cmd            cmd,
  419.     PF_InData        *in_data,
  420.     PF_OutData        *out_data,
  421.     PF_ParamDef        *params[],
  422.     PF_LayerDef        *output )
  423. {
  424.     PF_Err        err = PF_Err_NONE;
  425.     
  426.     EnterCodeResource();        /* MW specific */
  427.     
  428.     switch (cmd) {
  429.     case PF_Cmd_ABOUT:
  430.         err = About(in_data,out_data,params,output);
  431.         break;
  432.     case PF_Cmd_GLOBAL_SETUP:
  433.         err = GlobalSetup(in_data,out_data,params,output);
  434.         break;
  435.     case PF_Cmd_PARAMS_SETUP:
  436.         err = ParamsSetup(in_data,out_data,params,output);
  437.         break;
  438.     case PF_Cmd_GLOBAL_SETDOWN:
  439.         err = GlobalSetdown(in_data,out_data,params,output);
  440.         break;
  441.     case PF_Cmd_SEQUENCE_SETUP:
  442.         err = SequenceSetup(in_data,out_data,params,output);
  443.         break;
  444.     case PF_Cmd_SEQUENCE_SETDOWN:
  445.         err = SequenceSetdown(in_data,out_data,params,output);
  446.         break;
  447.     case PF_Cmd_SEQUENCE_RESETUP:
  448.         err = SequenceResetup(in_data,out_data,params,output);
  449.         break;
  450.     case PF_Cmd_RENDER:
  451.         err = Render(in_data,out_data,params,output);
  452.         break;
  453.     }
  454.  
  455.     ExitCodeResource();        /* MW specific */
  456.  
  457.     return err;
  458. }
  459.